`include "common_def.v"
module MODULE_ALU(
input								clk_i,
input								rst_i,
input								valid_i,
input [`WIDTH-1:0] 	A_i,
input [`WIDTH-1:0] 	B_i,
input [`OP_NUM-1:0] key_i,
input	[1:0]					mul_sign_key_i,
//output 							Carry_o,
//output 							Zero_o,
//output 							Overflow_o,
output [`WIDTH-1:0] Result_o,
output							result_valid_o
);
wire [`WIDTH-1:0] add_result;
wire [`WIDTH-1:0] and_result;
wire [`WIDTH-1:0] or_result;
wire [`WIDTH-1:0] xor_result;
wire [`WIDTH-1:0] sltu_result;
wire [`WIDTH-1:0] sll_result;
wire [`WIDTH-1:0] sra_result;
wire [`WIDTH-1:0] srl_result;
wire [`WIDTH-1:0] mul_result;
wire [`WIDTH-1:0] div_result;
wire [`WIDTH-1:0] rem_result;
//wire [`WIDTH-1:0] divu_result;
//wire [`WIDTH-1:0] remu_result;
wire [`WIDTH-1:0] slt_result;
wire [`WIDTH-1:0] mulh_result;


//产生加减相关信号
wire [`WIDTH-1:0] t_add_Cin;
wire C_in; //if C_in == 1 is sub
assign C_in = (key_i == `SUB)|(key_i == `SLT)|0 ;
assign t_add_Cin = ({`WIDTH{C_in}}^B_i) + {{(`WIDTH-1){1'b0}},C_in};
assign add_result = A_i + t_add_Cin;
//assign {Carry_o,add_result} = A_i + t_add_Cin;
//assign Overflow_o = (A_i[`WIDTH-1] == t_add_Cin[`WIDTH -1]) && (add_result[`WIDTH-1] != A_i[`WIDTH-1]);
//assign Zero_o = ~(|add_result);
//其他信号的计算
assign and_result = A_i&B_i;
assign or_result = A_i|B_i;
assign xor_result = A_i^B_i;
assign sltu_result = (A_i<B_i) ? 1:0;//use sub result to compare 
assign slt_result = add_result[`WIDTH-1]==1'b1 ? 1:0;//use sub result to compare 
assign sll_result = A_i << B_i;
assign sra_result = A_i >>> B_i;
assign srl_result = A_i >> B_i;
//assign mul_result = $signed($signed(A_i) * $signed(B_i));
//assign div_result = $signed($signed(A_i) /$signed(B_i));
//assign rem_result = $signed($signed(A_i)%$signed(B_i));
//assign divu_result = (A_i / B_i);
//assign remu_result = (A_i % B_i);
//利用mux来选择想要的计算结果
MuxKeyWithDefault #(`OP_NUM,`OP_NUM,`WIDTH) alu_mux (Result_o[`WIDTH-1:0],key_i[`OP_NUM-1:0],{`WIDTH{1'b0}},{
	`ADD	,	add_result[`WIDTH-1:0],
	`SLTU,	sltu_result[`WIDTH-1:0],
	`SLL	,	sll_result[`WIDTH-1:0],
	`XOR	,	xor_result[`WIDTH-1:0],
	`AND	,	and_result[`WIDTH-1:0],
	`SRA	,	sra_result[`WIDTH-1:0],
	`SUB	,	add_result[`WIDTH-1:0],
	`OR		,	or_result[`WIDTH-1:0],
	`SRL	,	srl_result[`WIDTH-1:0],
	`MUL	,	mul_result[`WIDTH-1:0],
	`DIV	,	div_result[`WIDTH-1:0],
	`REM	,	rem_result[`WIDTH-1:0],
	`DIVU ,	div_result[`WIDTH-1:0],
	`REMU	,	rem_result[`WIDTH-1:0],
	//`DIVU ,	divu_result[`WIDTH-1:0],
	//`REMU	,	remu_result[`WIDTH-1:0],
	`SLT	,	slt_result[`WIDTH-1:0],
	`MULH ,	mulh_result[`WIDTH-1:0]
});

//mult
wire							is_mul;
wire							mul_valid;
wire							mul_valid_r;
wire							mul_valid_i;
wire							mul_result_valid;
assign is_mul = (key_i[`OP_NUM-1:0] == `MUL);
assign mul_valid = valid_i & is_mul;
Reg #(1,0) mul_valid_reg(clk_i,rst_i,mul_valid,mul_valid_r,1);
assign mul_valid_i = mul_valid &(~mul_valid_r); //only one cycle
MODULE_MULT mult(
	.clk_i							(clk_i),
	.rst_i							(rst_i),	
	.mul_valid_i				(mul_valid_i),
	.mul_signed_i				(mul_sign_key_i[1:0]), //low bit 1 mean multiplicand_i should be seem as signed
  .multiplicand_i			(A_i[`WIDTH-1:0]),
  .multiplier_i				(B_i[`WIDTH-1:0]),
  .result_h_o					(mulh_result[`WIDTH-1:0]),
  .result_l_o 				(mul_result[`WIDTH-1:0]),
	.result_valid_o			(mul_result_valid)
);

wire							is_div;
wire							div_valid;
wire							div_valid_r;
wire							div_valid_i;
wire							div_result_valid;
wire							div_signed_i;
assign is_div = (key_i[`OP_NUM-1:0] == `DIV)|(key_i[`OP_NUM-1:0] == `DIVU)|(key_i[`OP_NUM-1:0] == `REM)|(key_i[`OP_NUM-1:0] == `REMU);
assign div_valid = valid_i & is_div;
Reg #(1,0) div_valid_reg(clk_i,rst_i,div_valid,div_valid_r,1);
assign div_valid_i = div_valid &(~div_valid_r); //only one cycle
assign div_signed_i =(key_i[`OP_NUM-1:0] == `DIV)| (key_i[`OP_NUM-1:0] == `REM);
MODULE_DIV div(
	.clk_i							(clk_i),
	.rst_i							(rst_i),
	.dividend_i					(A_i[`WIDTH-1:0]),
	.divisor_i					(B_i[`WIDTH-1:0]),
	.div_valid_i				(div_valid_i),
	.div_signed_i				(div_signed_i),
	.quotient_o					(div_result[`WIDTH-1:0]),
	.remainder_o				(rem_result[`WIDTH-1:0]),
	.result_valid_o			(div_result_valid)
);	

//choose the result_valid_o
wire [1:0] result_valid_key;
assign result_valid_key = {is_div,is_mul};
MuxKeyWithDefault #(3,2,1) result_valid_o_mux(result_valid_o,result_valid_key[1:0],1'b0,{
	2'b00,	valid_i,
	2'b01,	mul_result_valid,
	2'b10,	div_result_valid	
});
endmodule 
